package com.urea.web.core.config;

import com.alibaba.fastjson2.JSON;
import com.google.common.collect.Lists;
import com.urea.common.filter.RepeatedlyRequestWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.Ordered;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;

/**
 * @Description: Http请求日志过滤器
 * @Author: liuzd
 * @Date: 2023/10/25
 */
@Slf4j
@Component
public class HttpTraceLogFilter extends OncePerRequestFilter implements Ordered {

    // 配置要记录请求的路径前缀
    private static final String NEED_TRACE_PATH_PREFIX = "/";
    // 忽略为multipart/form-data的ContentType的请求
    private static final String IGNORE_CONTENT_TYPE = "multipart/form-data";

    private static final List<String> IGNORE_URL = Lists.newArrayList("/v2/**", "/v1/**");


    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE - 10;
    }

    @Override
    @SuppressWarnings("NullableProblems")
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (!isRequestValid(request)) {
            filterChain.doFilter(request, response);
            return;
        }
        String path = request.getRequestURI();
        HttpServletRequest wrapper = new RepeatedlyRequestWrapper(request,response);
        try {
            if (path.startsWith(NEED_TRACE_PATH_PREFIX) && !Objects.equals(IGNORE_CONTENT_TYPE, request.getContentType()) && !ignorePath(request)) {
                // 1. 记录日志
                consoleLog(path, wrapper);
            }
            filterChain.doFilter(wrapper, response);
        } finally {
            //  updateResponse(response);
        }
    }

    private boolean ignorePath(HttpServletRequest request) {
        return IGNORE_URL.stream().map(s -> new AntPathRequestMatcher(s)).anyMatch(e -> e.matches(request));
    }

    /**
     * @Description: 打印日志
     * @Param: [path - 请求路径, request - Http请求, startTime - 开始毫秒, status - 响应状态码, response - Http响应]
     * @Author: liuzd
     * @Date: 2023/10/25
     */
    private synchronized void consoleLog(String path, HttpServletRequest request) {
        log.info("-------------------- Api reqeust log start  --------------------");
        log.info("###请求开始 | 请求路径:[{}] | 请求方法:[{}] | 请求IP:[{}] | 请求参数:{} | 请求Body:{} | 请求Token:[{}], User={} ",
                path, request.getMethod(), request.getRemoteAddr(),
                JSON.toJSONString(request.getParameterMap()), getRequestBody(request),
                request.getHeader("Authorization"), request.getRemoteUser());
    }

    /**
     * @Description: 判断请求是否合法
     * @Param: [request]
     * @return: {@link boolean}
     * @Author: liuzd
     * @Date: 2023/10/25
     */
    private boolean isRequestValid(HttpServletRequest request) {
        try {
            new URI(request.getRequestURL().toString());
            return true;
        } catch (URISyntaxException ex) {
            return false;
        }
    }

    /**
     * @Description: 获取请求Body
     * @Param: [request]
     * @return: {@link String}
     * @Author: liuzd
     * @Date: 2023/10/25
     */
    private String getRequestBody(HttpServletRequest request) {
        String requestBody = "{}";
        try (InputStream is = request.getInputStream()) {
            return IOUtils.toString(is, StandardCharsets.UTF_8);
        } catch (IOException ex) {
            log.error("read http request failed", ex);
            return requestBody;
        }
    }

    /**
     * @Description: 获取响应Body
     * @Param: [response]
     * @return: {@link String}
     * @Author: liuzd
     * @Date: 2023/10/25
     */
    private String getResponseBody(HttpServletResponse response) {

        String responseBody = "{}";
        if (ignoreHeader(response)) {
            return responseBody;
        }
        ContentCachingResponseWrapper wrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        if (wrapper != null) {
            responseBody = IOUtils.toString(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8.name());
            responseBody = JSON.parseObject(responseBody).toJSONString();
        }
        return responseBody;
    }

    private boolean ignoreHeader(HttpServletResponse response) {
        return response.containsHeader("Content-Disposition");
    }

    /**
     * @Description: 更新响应
     * @Param: [response]
     * @Author: liuzd
     * @Date: 2023/10/25
     */
    private void updateResponse(HttpServletResponse response) throws IOException {
        ContentCachingResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, ContentCachingResponseWrapper.class);
        Objects.requireNonNull(responseWrapper).copyBodyToResponse();
    }

}